import { Models } from '@/common/typings/modle'
import Config from '@/config/Config'
import { AuthFailed, Forbidden } from '@/core/HttpException'

import JWT from 'jsonwebtoken'

import { Account } from '@/common/typings/account'
import { getTokenValue } from '@/server/redis'
import { getRedisUserPermission } from '@/server/auth'

export default async function verifyToken(ctx: Models.Ctx, next: Function, callback?: Function) {
  //获取token
  const userToken = getToken(ctx)
  if (!userToken || !(await getTokenValue(userToken)).results) {
    throw new Forbidden({ msg: '无访问权限' })
  }
  const { tokenInfo, scope } = (await analyzeToken(userToken)) as Account.Decode
  /**
   * tokenData 中的属性
   * id 当前用户id
   * userName
   */
  const tokenData = JSON.parse(tokenInfo as string)
  // 在上下文中保存uid和scope
  ctx.auth = {
    scope,
    ...tokenData,
  }
  if (callback) {
    await callback({ ...tokenData, scope })
  }
  await next()
}

/**
 * 获取token
 * @param ctx
 * @returns
 */
export function getToken(ctx: Models.Ctx): string {
  return ctx.header['authorization'] || ctx.cookies.get('authorization') || ''
}

/**
 * 解析token
 * @param token
 * @returns
 */
async function analyzeToken(token: string) {
  return new Promise((resolve, reject) => {
    JWT.verify(token, Config.SECURITY.SECRET_KEY, (error, decode) => {
      if (error) {
        reject(error)
      }
      resolve(decode)
    })
  }).catch((error) => {
    if (error.name === 'TokenExpiredError') {
      throw new AuthFailed({ msg: 'token已过期' })
    }
    throw new Forbidden({ msg: 'token不合法' })
  })
}

/**
 * 校验token权限
 * @param ctx
 * @param next
 */

export async function verifyTokenPermission(ctx: Models.Ctx, next: Function) {
  await verifyToken(ctx, next, async (decode: Account.Decode) => {
    // 获取当前角色的权限字段列表
    const permissionList: string[] = await getRedisUserPermission(decode)

    const bool = permissionList.find((permission) => {
      const path = `${Config.API_PREFIX}v1/${permission.split(':').join('/')}`
      return path === ctx.path
    })
    if (!bool) {
      throw new Forbidden({ msg: '权限不足' })
    }
  })
}
